﻿using System;
using Microsoft.Xna.Framework;

namespace xEngine.Actors
{
    public class Camera : Actor
    {
        #region Member Variables

        private float _fov;
        private float _aspect;
        private float _near;
        private float _far;

        private Vector2? _restrictX;
        private Vector2? _restrictY;
        private Vector2? _restrictZ;

        #endregion

        #region Properties

        public static Camera DefaultCamera { get; set; }

        public Matrix Projection { get; protected set; }
        public Matrix View { get; protected set; }

        public float FoV
        {
            get { return _fov; }
            set
            {
                _fov = value;
                OnProjetionMatrixUpdate();
            }
        }
        public float Aspect
        {
            get { return _aspect; }
            set
            {
                _aspect = value;
                OnProjetionMatrixUpdate();
            }
        }
        public float Near
        {
            get { return _near; }
            set
            {
                _near = value;
                OnProjetionMatrixUpdate();
            }
        }
        public float Far
        {
            get { return _far; }
            set
            {
                _far = value;
                OnProjetionMatrixUpdate();
            }
        }

        public Vector2? RestrictX
        {
            get { return _restrictX; }
            set
            {
                _restrictX = value;

                if (!_restrictX.HasValue)
                    return;

                Vector3 org = Transformation.Rotation;

                if (org.X < _restrictX.Value.X)
                    org.X = _restrictX.Value.X;
                if (org.X > _restrictX.Value.Y)
                    org.X = _restrictX.Value.Y;

                Transformation.Rotation = org;
            }
        }
        public Vector2? RestrictY
        {
            get { return _restrictY; }
            set
            {
                _restrictY = value;

                if (!_restrictY.HasValue)
                    return;

                Vector3 org = Transformation.Rotation;

                if (org.Y < _restrictY.Value.X)
                    org.Y = _restrictY.Value.X;
                if (org.Y > _restrictY.Value.Y)
                    org.Y = _restrictY.Value.Y;

                Transformation.Rotation = org;
            }
        }
        public Vector2? RestrictZ
        {
            get { return _restrictZ; }
            set
            {
                _restrictZ = value;

                if (!_restrictZ.HasValue)
                    return;

                Vector3 org = Transformation.Rotation;

                if (org.Z < _restrictZ.Value.X)
                    org.Z = _restrictZ.Value.X;
                if (org.Z > _restrictZ.Value.Y)
                    org.Z = _restrictZ.Value.Y;

                Transformation.Rotation = org;
            }
        }

        #endregion

        #region Constructors

        public Camera() : this(MathHelper.PiOver4, 1, 1, 10) { }
        public Camera(float fov, float aspect, float near, float far)
        {
            _fov = fov;
            _aspect = aspect;
            _near = near;
            _far = far;

            OnProjetionMatrixUpdate();
            OnViewMatrixUpdate();

            if(DefaultCamera == null)
                DefaultCamera = this;
        }

        #endregion

        #region Functions

        public override void Rotate(float x, float y)
        {
            Vector3 r = Transformation.Rotation;

            if (x != 0)
                r.X += x;

            if (y != 0)
                r.Y += y;

            if (_restrictX.HasValue)
            {
                if (r.X < _restrictX.Value.X) r.X = _restrictX.Value.X;
                if (r.X > _restrictX.Value.Y) r.X = _restrictX.Value.Y;
            }
            if (_restrictY.HasValue)
            {
                if (r.Y < _restrictY.Value.X) r.Y = _restrictY.Value.X;
                if (r.Y > _restrictY.Value.Y) r.Y = _restrictY.Value.Y;
            }

            Transformation.Rotation = r;
        }

        protected virtual void OnProjetionMatrixUpdate()
        {
            Projection = Matrix.CreatePerspectiveFieldOfView(FoV, Aspect, Near, Far);
        }
        protected virtual void OnViewMatrixUpdate()
        {
            View = Matrix.CreateLookAt(Transformation.Position, Transformation.Position + Transformation.Direction, Transformation.RotationM.Up);
        }
        protected override void OnTransformMatrixUpdate()
        {
            OnViewMatrixUpdate();
        }

        #endregion
    }
}
